[UArticle]使用Unity动画状态机制作流畅的动作游戏

您所在的位置:网站首页 transtion 动画 [UArticle]使用Unity动画状态机制作流畅的动作游戏

[UArticle]使用Unity动画状态机制作流畅的动作游戏

2023-04-14 19:16| 来源: 网络整理| 查看: 265

原作者:虚幻工作室21级成员  七鳃鳗Sherry 作者:UnrealStudio 

专栏功能提示:

长按点赞可一键三连哦~

我老早就该写的——离《Hotline》的提交已经过了近半年,我当时的想法是把当时实践和学习的一些知识整理、总结出来,免得后来的人像我这样在互联网翻来覆去的找教程,既搞心态又浪费时间;此外还能挽救一下本人起伏不定的情绪,提醒自己有学到过知识,有为其他人的学习提供方便。

《Hotline》是我在22年开发的一个动作游戏DEMO,也是一个“致敬”了《迈阿密热线》、《黑暗之魂》、《仁王》等经典游戏的,呃,缝合怪。为了让游戏手感流畅,动作衔接符合直觉,本人花了一些时间研究Unity的动画状态机,并且取得了一些成果。

下面讲一下我的开发过程吧。恕我偷懒(我的懒惰程度从我把这篇分享鸽了6个月这件事就能略窥一二),不会把创建Animator、编写控制器脚本这些流程都一一罗列出来,如果需要这些教程的话可以查看下图描述BV号中的视频。

同志们在搜索“如何使用Unity的动画状态机”,往往会看到这样的用法:

图源自UP主阿严Gaming发布的视频BV1jb4y1p7r5,一个很不错的Unity入门教程

把动画素材拖入Animator中,然后在角色控制器脚本中使用代码控制动画的播放与切换。这对于动画状态不多的物体来说挺方便也挺简单,但是:

图为《Hotline》主角的动画状态机中的攻击动作状态机(Sub-State Machine)。我把所有攻击动作相关的状态都塞进了这个动画层中。不同招式在输入不同命令的情况下可以派生不同的下一招,这12个动画状态之间互相连接,形成了错综复杂的拓扑结构。这12个动画状态还仅仅是攻击动作相关的状态。

要是用代码控制这些动画的播放与切换,把这些拓扑结构转换成面条代码,不用想就知道这种方法既编写麻烦又难以维护。我当然是不愿意当这个怨种的,想必大家也是一样。

所以我采用了图示的方法:用状态过渡(Transition)把动画状态连接起来,以直观的调整它们之间的关系。

过渡

讲讲状态过渡吧。这些带箭头的白线就是所谓的状态过渡,它将两个状态有方向的连接起来。

Q. A.

为什么需要状态过渡?如果你的动画模型有骨骼的话,效果收益会非常显著:它会为前后两个动画生成平滑的衔接动作,避免了从前个动画状态切换到下个动画状态时动画不自然的突变。让我们从上到下依次的看一看状态过渡的Inspector面板里的参数有什么作用吧:

Transitions:数组,记录着上个动画指向其他动画的所有状态过渡,为一个状态过渡勾选SOLO则禁用其他所有状态过渡,只能切换到该状态过渡指向的动画。MUTE同理。

Has Exit Time:如果不勾选则当切换条件(condition)被满足时就立刻切换为下一个状态;如果勾选,则即使切换条件满足时,也会在播放到Exit Time后才会切换到下一个状态。

需要注意的是,在勾选的情况下,如果切换条件满足时播放进度已经过了Exit Time,将不会切换到下一个状态。通俗的讲,当出招动画已经播放到后摇之后再输入指令,无法派生下一招。反面例子是黑魂,轻击一段后,即使收招快复位成站姿的时候再输入轻击指令,也会派生轻击二段,操作有种粘滞感。这种处理方式似乎叫输入延长?勾选Has Exit Time的同时设置Interruption Source似乎也可以做出“输入延长”。

Exit Time:退出当前动画状态的时间。数值是百分比。

Fixed Duration:不勾选时下列数值的单位是百分制,勾选后下列数值的单位是秒。

Transition Duration:切换到下一个动画状态时,将两个动画混合的平滑衔接动画的长度。

Transition Offset:切换到下一个动画状态时,从下一个动画的哪个时间开始。

Exit Time、Transition Duration、Transition Offset:也可以拖动下面的图表来调整。

本人对Interruption Source整得不是太明白,就不浪费口舌叙述我贫乏的拙见了,具体方法可以移步BV1xq4y147pD

 

那么就讲讲我做连招的动画状态机的思路吧。

 

首先给动画状态机设置参数。招式很多,但是出招指令无非是输入左键右键空格

不妨把玩家受到的指令和一些状态作为参数吧:

MB1(左键)、MB2(右键)、Space(空格)、CTRL(格挡)、Stun(受到伤害)、精力足够释放招式

从闲置状态过渡到轻击一段的切换条件设置成了CTRL:false,MB1:true

传入参数在控制器脚本里随便写写就行了吧,懒得列出来

从轻击一段到轻击二段:

我想模仿黑魂那样在后摇动画输入指令也能继续连段的“输入延长机制”。因为我的项目不需要用到骨骼动画,我选择给动画状态机增添一个Cancelable参数,当Cancelable为真意味着现在可以取消招式释放下一招。

那么Cancelable这个参数要怎么用呢?

不少动作类游戏会给角色的每一个招式上设置FLAG。

FLAG是干什么的呢?打个比方,骑士向前踏步挥舞大剑动画的总帧数是52帧;骑士在第5到15帧会迈腿,在此期间骑士会向前位移,同时能够调整攻击方向;在第21帧生成霸体;在第30帧骑士挥出大剑,生成攻击判定;第35帧攻击判定结束;从第38帧开始玩家可以输入下一个指令;如果玩家在第45帧前输入了指令,则会在第45帧取消当前动作后摇,派生下一击。

FLAG就是用来规定动画在什么时间执行什么任务的。

《尼尔》的动画FLAG调整工具,素材源自BV1A7411f78K

《尼尔》的动画FLAG调整工具,素材源自BV1A7411f78K

《黑暗之魂3》的动画编辑工具,素材源自并非NEP的专栏

动画帧事件

 我们Unity的可以在Animation窗口设置动画帧事件。它能够在指定帧数执行模型上挂载脚本中的函数。虽然略显简陋和抽象,但还是能够充当动画FLAG的作用的。(不知道这玩意儿是啥的还请自己百度一下吧)

我写了个叫做SetCancelable()的方法来获取Animator组件并将参数Cancelable设置为true,然后在动画帧事件里调用这个方法。这样当动画播放到动画帧事件所在的第33帧时,如果精力足够(控制器脚本里自己写一下判断条件吧,我写得很丑就不放出来了)、并且输入了MB1,则切换条件成立,动画切换到轻击二段。

 

题外话:物理帧数是每秒50帧,动画帧数是每秒60帧,而Transition Duration的单位是秒和百分号——这实在是混乱不堪,我打算学一下编辑器怎么写,把这些单位统一一下

 

这个时候我们发现虽然能够衔接,但是手感稀烂:喜欢狂沁左键输出的同志会发现如果站立时摁了两下左键,第二段轻击也会不受控制的A出来。有两种解决办法:

1. 写一个指令预输入机制。比如做一个队列,按下左键MB1会被设置true,在0.2s后将MB1设置False。为保证手感只能预输入一个指令。之类的。

2. 使用动画帧事件,在某个时间点之前输入的指令无效。

 

我选用了方案2。写了个AllowSignalInput()方法,里面将MB1、MB2之类的所有指令设置为False。

这样在第25帧之前输入的指令都无效,第25帧后输入的指令会在第32帧或32帧之后执行。

 

像设置转向修正、设置攻击判定之类的FLAG也可以用动画帧事件。

 

 需要注意的是,如果动画帧事件挂载的那一帧不会播放的话,事件自然也不会触发。有人喜欢在动画的末尾挂载一个动画帧事件,状态过渡中Exit Time设置得比较早的,往往还没播放到结尾就已经切换到下一个动画状态了。需要在动画结束时挂载代码的可以使用下文脚本的OnStateExit().

 

状态机行为脚本 State Machine Behaviour

 

动画状态上也是可以挂载脚本的。继承自StateMachineBehaviour的类提供了三个默认方法OnStateEnter() OnStateExit() OnStateUpdate(),另外因为该脚本属于资源对象(?),不能在该类的全局变量里获取场景对象,但是可以从这几个方法传入的Animator获取场景对象。

 

我编写了三个脚本

FSM State Action Data用于存储招式数据,在切换到此状态时在OnStateEnter()把招式数据发给控制器脚本,还挺直观方便。为了能让控制器判断是否角色有精力出下一招,我把左键派生的招式和右键派生的招式也塞了进去。不知道有没有更优雅的解决方法?

FSM Clear Signal 我会在脚本里声明一个数组,把需要在OnStateEnter()设置为False的参数塞进去,减少玩家搓招时的误操作。

FSM Enter Action State 用来向控制器的状态机传递状态。

 

需要注意的是,在控制器脚本中使用getComponent().getBehaviour()返回的不是动画状态机当前所处的动画状态所挂载的那个脚本,至于返回的是哪个我也没弄明白,Unity API文档中写得也是含糊其辞模棱两可,很气人

任意动画状态Any State

一般玩家不做动作的时候,总是会复原到闲置站姿对吧?不管玩家在干什么,挨打的时候都会立刻切换到受击动画对吧?手动把所有动画状态都连回闲置站姿会让状态机变成乱七八糟的蜘蛛网。用Any State吧!

用Any State连接Stun状态,设置一个切换条件,不管在任何状态,只要条件满足的时候都能切换到Stun状态。处在受击硬直时受击当然会再吃一个受击硬直,所以勾选Can Transition To Self。

用Any State

连接到Idle(闲置站立)状态,勾选Has Exit Time,取消勾选Can Transition To Self,就能让玩家在不进行动作时自然回到Idle(闲置站立)状态。

 

如果希望某个状态不能从Any State切换到另一个状态,多设置一个切换条件就好了。

动画层Layer

当你需要角色同时做两个动作的时候,可以设置其他的动画层。一些FPS游戏里会把上半身和下半身分开在两个动画层级里;上半身持枪动画,下半身行走跳跃站立,同时进行。2D游戏则可以把本身动作和受击闪烁分开。

 

另外是一些略微不太常见但是常用的函数

Animator.GetCurrentAnimatorStateInfo(int LayerIndex)。会返回当前的动画状态。返回的这个AnimatorStateInfo类无法以字符串形式得到状态的名称,但是可以用IsName(String name)或IsTag(String tagName)来判断是否是这个名称或有该标签。哦对,动画状态上是可以打标签的。

 

LayerIndex是动画层编号。Base Layer是0,其他动画层的编号从上到下依次增大。

 

Animator.GetNextAnimatorStateInfo(int LayerIndex)。当动画处在状态过渡Transition中,会返回该状态过渡指向的下一个动画状态。

 

2D混合树建议自己了解一下。

 

尽管这些思路可能不是很优雅,但大概把经验分享出来,大概也能让别人少走一些弯路。我的语言表达能力不是很好,可能文章不是很通顺。

希望能够对你有所帮助!

感谢观看,喜欢的话可以一键三连哦~

素材整理&编辑:七鳃鳗Sherry

发布:乀艨hansa

B站账号:UnrealStudio

工作室官网:unrealstudio.github.io



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3